home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / tools / cie.lha / cie / etags++ / etags++.c < prev    next >
C/C++ Source or Header  |  1993-06-21  |  13KB  |  551 lines

  1. ////////////////////////////////////////////////////////////////////////////////
  2. // etags++.c
  3. //
  4. // This program reads in C++ code and generates a tags file for GNU Emacs.
  5. // This program is more sophisticated than the standard etags program for C++
  6. // programs.  It finds all classes, structs, unions, enums, enumerators, #defines,
  7. // typedefs, functions (both global and member), and data (both global and member).
  8. // Furthermore, it handles C++ scoping correctly, outputting fully-scoped tags
  9. // at the end of each line.  Thus, we have modified our Emacs tags.el to search
  10. // the fully-scoped names at the ends of the lines before the patterns.  It also
  11. // handles template syntax.
  12. //
  13. // In addition, we have added support for a few important macro conventions that
  14. // we use.  DECLARE_*(name,..) macros define the tag <name> in the current scope;
  15. // DEFINE_*(class,name,...) macros define the tag <class>::<name>.  We use NOTE(name)
  16. // macros to name comments, so that you can refer to them by See::name in other
  17. // comments.  In Emacs, M-. on See::name will take you to the named comment.
  18. //
  19. // Note that this uses "fuzzy", quick-and-dirty parsing to find the tokens.  Thus, it
  20. // can miss some things.  Also note that this is not an etags replacement -- it only
  21. // supports C/C++ code.  The etags program will still be needed for TeX, Fortran, etc.
  22. //
  23. // Author:  Brian M. Kennedy
  24. // (C) Copyright 1993, Intellection Inc.
  25. // Permission is granted to use, copy, or modify this code as long as this author and
  26. // copyright notice is maintained in all copies.
  27. //
  28. // Note:
  29. //   This is quick, hack code that was not written to be modifiable or maintainable -- beware!!
  30. //   I would not allow code such as this into our product!  But it is okay for a quick tool hack.
  31. //   If you are a user, I hope you enjoy it.  If you are modifier, my apologies ;-(
  32.  
  33. #include "c++file.c"
  34.  
  35.  
  36. ////////////////////////////////////////////////////////////////////////////////
  37.  
  38. struct Tag;
  39.  
  40. struct Scope
  41. {
  42.   C_File_Pos name;
  43.   Scope*     next;
  44.  
  45.   Scope (const Scope& copy);
  46.   Scope (const C_File_Pos& name_arg, Scope* next_arg);
  47.   ~Scope () { delete next; }
  48.  
  49.   Scope* pop ();
  50.  
  51.   unsigned etags_size () const;
  52.   void     etags_put  (ostream& os) const;
  53. };
  54.  
  55. inline Scope* copy (const Scope* s);
  56.  
  57.  
  58. Scope::
  59. Scope (const Scope& s)
  60. :name(s.name), next(copy(s.next))
  61. {}
  62.  
  63.  
  64. Scope::
  65. Scope (const C_File_Pos& name_arg, Scope* next_arg)
  66. :name(name_arg), next(copy(next_arg))
  67. {}
  68.  
  69.  
  70. inline Scope* Scope::
  71. pop ()
  72. { Scope* ret = next;
  73.   next = 0;
  74.   delete this;
  75.   return ret;
  76. }
  77.  
  78.  
  79. unsigned Scope::
  80. etags_size () const
  81. { if(this)
  82.     return name.length + 2 + next->etags_size();
  83.   else
  84.     return 0;
  85. }
  86.  
  87.  
  88. void Scope::
  89. etags_put  (ostream& os) const
  90. { if(this)
  91.   { next->etags_put(os);
  92.     os.write(name.chars(), name.length);
  93.     os << "::";
  94.   }
  95. }
  96.  
  97.  
  98. inline Scope*
  99. copy (const Scope* s)
  100. { return s ? new Scope (*s) : 0; }
  101.  
  102.  
  103. ////////////////////////////////////////////////////////////////////////////////
  104. // Prefixes
  105.  
  106. char* prefix_string = " See EXPORTED set_ inc_ dec_";
  107.  
  108. File  prefix_file (strlen(prefix_string), prefix_string);
  109.  
  110. C_File_Pos prefix_pos (prefix_file);
  111. C_File_Pos see_pos    ((prefix_pos.next_identifier(), prefix_pos));
  112. C_File_Pos exported_pos ((prefix_pos.next_identifier(), prefix_pos));
  113. C_File_Pos set_pos    ((prefix_pos.next_identifier(), prefix_pos));
  114. C_File_Pos inc_pos    ((prefix_pos.next_identifier(), prefix_pos));
  115. C_File_Pos dec_pos    ((prefix_pos.next_identifier(), prefix_pos));
  116.  
  117. Scope  see_scope_obj (see_pos, 0);
  118. Scope* see_scope = &see_scope_obj;
  119.  
  120. Scope  exported_scope_obj (exported_pos, 0);
  121. Scope* exported_scope = &exported_scope_obj;
  122.  
  123.  
  124. ////////////////////////////////////////////////////////////////////////////////
  125.  
  126. struct Tag
  127. {
  128.   Tag*       next;
  129.   C_File_Pos name;
  130.   File_Pos   pattern;
  131.   Scope*     scope;
  132.  
  133.   Tag (const Tag& t);
  134.   Tag (const C_File_Pos& name_arg, const Scope* scope_arg);
  135.  
  136.   ~Tag () { delete scope; }
  137.  
  138.   unsigned etags_size () const;
  139.   void     etags_put  (ostream& os) const;
  140. };
  141.  
  142. Tag::
  143. Tag (const Tag& t)
  144. :next(0), name(t.name), pattern(t.pattern), scope(copy(t.scope))
  145. {}
  146.  
  147. Tag::
  148. Tag (const C_File_Pos& name_arg, const Scope* scope_arg)
  149. :next(0), name(name_arg), pattern(name_arg), scope(copy(scope_arg))
  150. { pattern.find_prev_newline();
  151. }
  152.  
  153.  
  154. unsigned Tag::
  155. etags_size () const
  156. { return ((name.length + pattern.char_no - name.char_no)
  157.       + 1 + size(name.line_no) + 1 + size(name.char_no) + 2
  158.       + scope->etags_size() + name.length + 1);
  159. }
  160.  
  161.  
  162. void Tag::
  163. etags_put (ostream& os) const
  164. { os.write(pattern.chars(), name.length + name.char_no - pattern.char_no);
  165.   os << '\177' << name.line_no << ',' << name.char_no << ",\1";  
  166.   scope->etags_put(os);
  167.   os.write(name.chars(), name.length);
  168.   os << '\n';
  169. }
  170.  
  171.  
  172. ////////////////////////////////////////////////////////////////////////////////
  173.  
  174. struct Tag_List
  175. {
  176.   Tag* first;
  177.   Tag* last;
  178.  
  179.   Tag_List () :first(0), last(0) {}
  180.   ~Tag_List ();
  181.  
  182.   void inc (Tag* tag);
  183.  
  184.   unsigned etags_size () const;
  185.   void     etags_put  (ostream& os) const;
  186. };
  187.  
  188.  
  189. Tag_List::
  190. ~Tag_List ()
  191. { Tag* tag = first;
  192.   while(tag)
  193.   { first = tag->next;
  194.     delete tag;
  195.     tag = first;
  196.   }
  197.   last = 0;
  198. }
  199.  
  200.  
  201. inline void Tag_List::
  202. inc (Tag* tag)
  203. { if(last)
  204.   { last->next = tag;
  205.     last = tag;
  206.   }
  207.   else
  208.   { first = last = tag;
  209.   }
  210. }
  211.  
  212.  
  213. unsigned Tag_List::
  214. etags_size () const
  215. { unsigned sum = 0;
  216.   for(Tag* tag = first; tag; tag = tag->next)
  217.     sum += tag->etags_size();
  218.   return sum;
  219. }
  220.  
  221.  
  222. void Tag_List::
  223. etags_put  (ostream& os) const
  224. { for(Tag* tag = first; tag; tag = tag->next)
  225.     tag->etags_put(os);
  226. }
  227.  
  228.  
  229. ostream&
  230. operator << (ostream& os, Tag_List* tags)
  231. { tags->etags_put(os);
  232.   return os;
  233. }
  234.  
  235.  
  236. ////////////////////////////////////////////////////////////////////////////////
  237.  
  238. Tag_List*
  239. get_tags (const File& file)
  240. {
  241.   Tag_List*  tags = new Tag_List ();
  242.   Scope*     scope = 0;
  243.   Scope*     qualified = 0;
  244.   C_File_Pos pos (file);
  245.   C_File_Pos prev_id (pos);
  246.   while(pos.token != END_OF_FILE)
  247.   {
  248.     switch(pos.token)
  249.     {
  250.     case CLASS_KW:
  251.     case STRUCT_KW:
  252.     case UNION_KW:
  253.       pos.next_code();
  254.       if(pos.token == IDENTIFIER)
  255.       { C_File_Pos tag_name (pos);
  256.     do
  257.     { pos.next_code();
  258.     } while(pos.token != SEMI_COLON && pos.token != OPEN_BRACE && pos.token != END_OF_FILE);
  259.     if(pos.token == OPEN_BRACE)
  260.     { tags->inc(new Tag(tag_name, scope));
  261.       scope = new Scope(tag_name, scope);
  262.     }
  263.       }
  264.       else
  265.       { while(pos.token != SEMI_COLON && pos.token != OPEN_BRACE && pos.token != END_OF_FILE)
  266.       pos.next_code();
  267.     if(pos.token == OPEN_BRACE)
  268.       pos.close_brace();
  269.       }
  270.       break;
  271.     case ENUM_KW:
  272.       pos.next_code();
  273.       if(pos.token == IDENTIFIER)
  274.       { C_File_Pos tag_name (pos);
  275.     do
  276.     { pos.next_code();
  277.     } while(pos.token != SEMI_COLON && pos.token != OPEN_BRACE && pos.token != END_OF_FILE);
  278.     if(pos.token == OPEN_BRACE)
  279.       tags->inc(new Tag (tag_name, scope));
  280.       }
  281.       if(pos.token == OPEN_BRACE)
  282.       { while(pos.token != CLOSE_BRACE && pos.token != END_OF_FILE)
  283.     { pos.next_code();
  284.       if(pos.token == IDENTIFIER)
  285.         tags->inc(new Tag (pos, scope));
  286.       do
  287.       { pos.next_code();
  288.       } while(pos.token != COMMA && pos.token != CLOSE_BRACE && pos.token != END_OF_FILE);
  289.     }
  290.       }
  291.       while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
  292.     pos.next_code();
  293.       break;
  294.     case TYPEDEF_KW:        // only catches last typedef (e.g 'c' in typedef int a, b, c;)
  295.     { C_File_Pos next (pos);
  296.       next.next_code();
  297.       do
  298.       { pos = next;
  299.     next.next_code();
  300.       } while(next.token != SEMI_COLON && next.token != OPEN_PARE && next.token != END_OF_FILE);
  301.       
  302.       if(next.token == OPEN_PARE && next.chars(1) == '*')
  303.       { // Function Typedef
  304.     next.next_code();
  305.     if(next.token == IDENTIFIER)
  306.       tags->inc(new Tag (next, scope));
  307.       }
  308.  
  309.       while(next.token != SEMI_COLON && next.token != END_OF_FILE)
  310.       { pos = next;
  311.     next.next_code();
  312.       }
  313.  
  314.       if(pos.token == IDENTIFIER)
  315.     tags->inc(new Tag (pos, scope));
  316.       pos = next;
  317.     }
  318.       break;
  319.     case DEFINE:
  320.       pos.next_code();
  321.       if(pos.token == IDENTIFIER || pos.token == DECLARE_MACRO || pos.token == DEFINE_MACRO)
  322.     tags->inc(new Tag (pos, scope));
  323.       pos.close_define();
  324.       break;
  325.     case CLOSE_BRACE:
  326.       if(scope) scope = scope->pop();
  327.       break;
  328.     case OPEN_BRACE:
  329.       pos.close_brace();
  330.       break;
  331.     case OPEN_PARE:
  332.       if(prev_id.token == IDENTIFIER)
  333.       { tags->inc(new Tag(prev_id, qualified));
  334.     prev_id = pos;
  335.       }
  336.       pos.close_pare();
  337.       pos.close_func();
  338.       break;
  339.     case COLONS:
  340.       if(prev_id.token == IDENTIFIER)
  341.       { qualified = new Scope(prev_id, qualified);
  342.     prev_id = pos;
  343.       }
  344.       break;
  345.     case IDENTIFIER:
  346.       if(prev_id.token != COLONS)
  347.       { delete qualified;
  348.     qualified = copy(scope);
  349.       }
  350.       prev_id = pos;
  351.       break;
  352.     case SEMI_COLON:
  353.       if(prev_id.token == IDENTIFIER)
  354.       { tags->inc(new Tag(prev_id, qualified));
  355.     prev_id = pos;
  356.       }
  357.       break;
  358.     case EQUAL:
  359.       if(prev_id.token == IDENTIFIER)
  360.       { tags->inc(new Tag(prev_id, qualified));
  361.     prev_id = pos;
  362.       }
  363.       pos.close_func();
  364.       break;
  365.     case NOTE:
  366.       pos.next_code();
  367.       if(pos.token == OPEN_PARE)
  368.       { pos.next_code();
  369.     if(pos.token == IDENTIFIER)
  370.       tags->inc(new Tag(pos, see_scope));
  371.     if(pos.token != CLOSE_PARE)
  372.       pos.close_pare();
  373.       }
  374.       while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
  375.     pos.next_code();
  376.       break;
  377.     case EXPORT:
  378.       pos.next_code();
  379.       if(pos.token == OPEN_PARE)
  380.       { do
  381.     { pos.next_code();
  382.     } while (pos.token != COMMA && pos.token != END_OF_FILE);
  383.     pos.next_code();
  384.     if(pos.token == IDENTIFIER)
  385.       tags->inc(new Tag(pos, exported_scope));
  386.     if(pos.token != CLOSE_PARE)
  387.       pos.close_pare();
  388.       }
  389.       while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
  390.     pos.next_code();
  391.       break;
  392.     case DECLARE_MACRO:
  393.       pos.next_code();
  394.       if(pos.token == OPEN_PARE)
  395.       { pos.next_code();
  396.     if(pos.token == IDENTIFIER)
  397.       tags->inc(new Tag(pos, scope));
  398.     if(pos.token != CLOSE_PARE)
  399.       pos.close_pare();
  400.       }
  401.       while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
  402.     pos.next_code();
  403.       break;
  404.     case DEFINE_MACRO:
  405.       pos.next_code();
  406.       if(pos.token == OPEN_PARE)
  407.       { pos.next_code();
  408.     if(pos.token == IDENTIFIER)
  409.     { qualified = new Scope (pos, 0);
  410.       while(pos.token != COMMA && pos.token != CLOSE_PARE && pos.token != END_OF_FILE)
  411.         pos.next_code();
  412.       if(pos.token == COMMA)
  413.       { pos.next_code();
  414.         if(pos.token == IDENTIFIER)
  415.           tags->inc(new Tag(pos, qualified));
  416.       }
  417.       qualified = qualified->pop();
  418.     }
  419.     if(pos.token != CLOSE_PARE)
  420.       pos.close_pare();
  421.       }
  422.       break;
  423.     case DEFINE_GET:
  424.     case DEFINE_GETSET:
  425.       pos.next_code();
  426.       if(pos.token == OPEN_PARE)
  427.       { while(pos.token != COMMA && pos.token != CLOSE_PARE && pos.token != END_OF_FILE)
  428.       pos.next_code();
  429.     if(pos.token == COMMA)
  430.     { pos.next_code();
  431.       if(pos.token == IDENTIFIER)
  432.           tags->inc(new Tag(pos, scope));
  433.     }
  434.     if(pos.token != CLOSE_PARE)
  435.       pos.close_pare();
  436.       }
  437.       break;
  438.     case DEFINE_SET:
  439.     case DEFINE_INC:
  440.     case DEFINE_DEC:
  441.       pos.next_code();
  442.       if(pos.token == OPEN_PARE)
  443.         pos.close_pare();
  444.       break;
  445.     default:
  446.       ;
  447.     }
  448.     pos.next_code();
  449.   }
  450.   return tags;
  451. }
  452.  
  453.  
  454. ////////////////////////////////////////////////////////////////////////////////
  455.  
  456. void
  457. usage_error (const char* progname)
  458. { cerr << "Usage " << progname << " [-a] [-f outfile] [-k max_infile_kbytes] infile ..."
  459.     << endl;
  460.   exit(BAD);
  461. }
  462.  
  463.  
  464. main (int argc, char** argv)
  465. {
  466.   unsigned argi = 0;
  467.   char* progname = argv[argi];
  468.   
  469.   // Default flags
  470.   Boolean     append  = FALSE;
  471.   const char* outfile = 0;
  472.   int         size    = 1024;
  473.   
  474.   // Process flags
  475.   for(argi = 1; argi < argc && argv[argi][0] == '-'; ++argi)
  476.   {
  477.     Boolean done = FALSE;
  478.     for(unsigned chari = 1; !done && argv[argi][chari]; ++chari)
  479.     {
  480.       switch(argv[argi][chari])
  481.       {
  482.       case '?':
  483.       case 'h':
  484.     usage_error(progname);
  485.       case 'a':
  486.     append = TRUE;
  487.     break;
  488.       case 'f':
  489.     if(outfile)
  490.     { cerr << "The -f option may only be given once." << endl;
  491.       usage_error(progname);
  492.     }
  493.     if(argv[argi][chari+1])
  494.     { done = TRUE;
  495.       outfile = &argv[argi][chari+1];
  496.     }
  497.     else if(argi < argc)
  498.     { done = TRUE;
  499.       outfile = argv[++argi];
  500.     }
  501.     else
  502.     { cerr << "The -f option must be given an argument (the outfile name)" << endl;
  503.       usage_error(progname);
  504.     }
  505.     break;
  506.       case 'k':
  507.     if(argv[argi][chari+1])
  508.     { done = TRUE;
  509.       size = atoi(&argv[argi][chari+1]);
  510.     }
  511.     else if(argi < argc)
  512.     { done = TRUE;
  513.       size = atoi(argv[++argi]);
  514.     }
  515.     else
  516.     { cerr << "The -k option must be given an argument (the max_file_size in kbytes)" << endl;
  517.       usage_error(progname);
  518.     }
  519.     break;
  520.       default:
  521.     ;
  522.       }
  523.     }
  524.   }
  525.   
  526.   // Arg value checks
  527.   if(size < 64)
  528.     size = 64;
  529.   if(!outfile)
  530.     outfile = "TAGS";
  531.  
  532.   // Create output TAGS file
  533.   ofstream out (outfile, (append ? ios::app : ios::out));
  534.  
  535.   // Create File for input
  536.   File infile (size*1024);
  537.  
  538.   // Process files
  539.   for(; argi < argc; ++argi)
  540.   {
  541.     infile.read(argv[argi]);
  542.     Tag_List* tags = get_tags(infile);
  543.     out << "\f\n" << argv[argi] << ',' << tags->etags_size() << '\n' << tags;
  544.     delete tags;
  545.   }
  546.  
  547.   out << flush;
  548.  
  549.   exit(GOOD);
  550. }
  551.